/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#include "RingBuffer.h"
#include <iostream>
#include <string.h>

namespace libemb{
RingBuffer::RingBuffer():
m_startPos(0),
m_endPos(0),
m_occupant(0),
m_capacity(0),
m_buffer(NULL)
{
}

RingBuffer::~RingBuffer()
{
	if (m_buffer!=NULL)
	{
		delete [] m_buffer;
	}
}

bool RingBuffer::init(int capacity)
{
	if (m_buffer==NULL && capacity>0)
	{
		m_buffer = NEW_OBJ char[capacity];
		if (m_buffer!=NULL)
		{
			m_capacity = capacity;
			return true;
		}
	}
	return false;
}

void RingBuffer::reset()
{
	AutoLock lock(&m_lock);
	m_startPos=0;
	m_endPos = 0;
	m_occupant = 0;
}

int RingBuffer::capacity()
{
	return m_capacity;
}

int RingBuffer::space()
{
	return m_capacity-m_occupant;
}

int RingBuffer::size()
{
	return m_occupant;
}

int RingBuffer::read(char *data, int bytes)
{
	if (bytes == 0) 
	{
		return 0;
	}
	AutoLock lock(&m_lock);
	int capacity = m_capacity;
	int bytesToRead = MIN(bytes, m_occupant);
	if (bytesToRead <= capacity - m_startPos)
	{
		memcpy(data, m_buffer + m_startPos, bytesToRead);
		m_startPos += bytesToRead;
		if (m_startPos == capacity)
		{
			m_startPos = 0;
		}
	}
	else
	{
		int sizeP1 = capacity - m_startPos;
		int sizeP2 = bytesToRead - sizeP1;
		memcpy(data, m_buffer + m_startPos, sizeP1);
		memcpy(data + sizeP1, m_buffer, sizeP2);
		m_startPos = sizeP2;
	}

	m_occupant -= bytesToRead;
	return bytesToRead;
}

int RingBuffer::write(char *data, int bytes)
{
	if (bytes == 0) 
	{
		return 0;
	}
	AutoLock lock(&m_lock);
	int capacity = m_capacity;
	int bytesToWrite = MIN(bytes, capacity-m_occupant);
	if (bytesToWrite <= capacity - m_endPos)
	{
		memcpy(m_buffer + m_endPos, data, bytesToWrite);
		m_endPos += bytesToWrite;
		if (m_endPos == capacity) 
		{
			m_endPos = 0;
		}
	}
	else
	{
		int sizeP1 = capacity - m_endPos;
		int sizeP2 = bytesToWrite - sizeP1;
		memcpy(m_buffer + m_endPos, data, sizeP1);
		memcpy(m_buffer, data + sizeP1, sizeP2);
		m_endPos = sizeP2;
	}

	m_occupant += bytesToWrite;
	return bytesToWrite;
}

}
